from __future__ import absolute_import
import subprocess
import tempfile
import solv
import os
import re
import logging
import time
from pkgr.util import mkdir_p
logger = logging.getLogger('pkgr.RepoGeneric')

class RepoGeneric(object):
    handle = None
    _enabled = True
    _cache_dir = '/var/lib/solv'
    _root_dir = "/"
    def get_groups(self):
        return None
    def __init__(self, name, type, attribs = {}):
        # TODO: remove type, pass attribs as kwargs
        self.attribs = attribs
        self.name = name
        self.type = type
        
        for k in attribs:
            self.set_prop(k, attribs[k])
    
    def __hash__(self):
        ''' Repos are unique by their name.
        '''
        return hash(self.get_name())
    
    def __eq__(self, other):
        return hash(self) == hash(other)
    
    def add_too_pool(self, pool):
        ''' Add the repository to the pool.
        
        '''
        logger.info('%s: adding to pool', self)
        handle = pool.add_repo(self.get_name())
        self.set_solv_handle(handle)
        
        self.get_solv_handle().appdata = self
        self.get_solv_handle().priority = 99 - self.get_priority()
        
        logger.debug('Priority %s', self.get_solv_handle().priority)
        logger.debug('Cachepath %s', self.cachepath())
        
        # do actual expiration check here
        self.load_if_changed()
        logger.debug('%s: expired: %s', self, self.is_expired())
    
    def load_if_changed(self):
        return False

    def load_ext(self, repodata):
        return False
        
    def get_solv_handle(self):
        return self.handle
    
    def set_solv_handle(self, h):
        self.handle = h
        
    def get_enabled(self):
        ''' Returns True if this repository is enabled.
        '''
        return self._enabled
        
    def get_priority(self):
        ''' Returns this repos priority from 0-99.
        '''
        return self.get_prop('priority', 99)
        
    def get_type(self):
        return self.type
        
    def get_name(self):
        return self.name
        
    def has_prop(self, p):
        return p in self.attribs
        
    def get_prop(self, *args):
        return self.attribs.get(*args)
     
    def set_prop(self, k, v):
        self.attribs[k] = v
        
      
    def get_expiration_delta(self):
        ''' Returns number of seconds it takes for the repo to expire.
        '''
        # XXX: HARDCODED EXPIRES
        return self.get_prop('metadata_expire')
        
    def set_cache_dir(self, p):
        self._cache_dir = p

    def get_cache_dir(self):
        cdir = self._cache_dir
        if not os.path.exists(self._cache_dir):
            mkdir_p(self._cache_dir)
        return self._cache_dir

    def join_cache_dir(self, *args):
        return os.path.join(self.get_cache_dir(), *args) 
     
    def set_root_dir(self, p):
        self._root_dir = p

    def get_root_dir(self):
        return self._root_dir
        
    def join_root_dir(self, *args):
        return os.path.join(self.get_root_dir(), *args) 
    
    def cachepath(self, ext = None):
        ''' Deprecated joiner. '''
        path = re.sub(r'^\.', '_', self.get_name())
        if ext:
            path += "_" + ext + ".solvx"
        else:
            path += ".solv"
        return self.join_cache_dir(self.get_cache_dir(), re.sub(r'[/]', '_', path))
        
    def __repr__(self):
        return '<{0}({1})>'.format(self.__class__.__name__, self.get_name())
    

    def is_cache_expired(self):
        expired = True
        cachepath = self.get_cachepath()
        if os.path.exists(cachepath):
            st = os.stat(cachepath)
            diff = time.time() - st.st_mtime
            
            if diff <= self.get_expiration_delta():
                logger.debug('%s: Cache ok. Expires in %s seconds.', self, self.get_expiration_delta()- diff)
                return False
            
            logger.debug('%s: Cache %s. Diff: %s. Expires %s. ', self, cachepath, diff, self.get_expiration_delta())
        return True
        
    def writecachedrepo(self, ext, info=None):
        ''' Writes the repository solv to the cache dir.
        '''
        try:
            destination =  self.cachepath(ext)
            logger.debug('Writing cached repo to %s', destination)
            
            cachedir = self.get_cache_dir()
            if not os.path.isdir(cachedir):
                mkdir_p(cachedir)
                os.chmod(cachedir, 0755)
                
            (fd, tmpname) = tempfile.mkstemp(prefix='.newsolv-', dir=self.get_cache_dir())
            os.fchmod(fd, 0444)
            f = os.fdopen(fd, 'w+')
            if not info:
                self.get_solv_handle().write(f)
            elif ext:
                info.write(f)
            else:       # rewrite_repos case
                self.get_solv_handle().write_first_repodata(f)
            f.close()
            
            if self.get_solv_handle().iscontiguous():
                # switch to saved repo to activate paging and save memory
                self._load_solv_repo(tmpname, ext, info)
                
            # atomic to dest
            os.rename(tmpname, destination)
        except IOError, e:
            if tmpname:
                os.unlink(tmpname)
   
    def _load_solv_repo(self, tmpname, ext, info):
        # switch to saved repo to activate paging and save memory
        nf = solv.xfopen(tmpname)
        if not ext:
            # main repo
            self.get_solv_handle().empty()
            if not self.get_solv_handle().add_solv(nf, solv.Repo.SOLV_ADD_NO_STUBS):
                raise Exception("internal error, cannot reload solv file for repo %s. Please clear your caches." % self.get_name())
        else:
            # extension repodata
            # need to extend to repo boundaries, as this is how
            # info.write() has written the data
            info.extend_to_repo()
            # LOCALPOOL does not help as pool already contains all ids
            info.add_solv(nf, solv.Repo.REPO_EXTEND_SOLVABLES)
        solv.xfclose(nf)
        
    def updateaddedprovides(self, addedprovides):
        ''' Unused.
        '''
        ## XXX: not sure what this is for. from the libsolv example
        return
        if self.get_solv_handle() is None:
            return
        if self.get_solv_handle().isempty():
            return
        # make sure there's just one real repodata with extensions
        repodata = self.get_solv_handle().first_repodata()
        if not repodata:
            return
        oldaddedprovides = repodata.lookup_idarray(solv.SOLVID_META, solv.REPOSITORY_ADDEDFILEPROVIDES)
        if not set(addedprovides) <= set(oldaddedprovides):
            for id in addedprovides:
                repodata.add_idarray(solv.SOLVID_META, solv.REPOSITORY_ADDEDFILEPROVIDES, id)
            repodata.internalize()
            logger.debug('Updated added provides, rewrite %s %s', self.get_name(), repodata)
            self.writecachedrepo(None, repodata)
